Go to the first, previous, next, last section, table of contents.

Kernel

The kernel is the part of Xconq shared by all interfaces. It does no I/O except to files or for debugging.

Specifically, the kernel supplies the following functionality:

Configuration Options

There are a small number of options available to alter aspects of the kernel. These are defined and described in kernel/config.h. It is unlikely that you will ever need to change any of these.

Porting the Kernel

The kernel should be restricted to ANSI C, and should avoid or optionalize features not in "traditional" C, such as prototypes. Although the kernel uses stdio, it does not assume the presence of a console (stdin, stdout, stderr). For instance, a graphical interface can arrange to disable stdin entirely and direct stdout/stderr into a file (see the Mac interface sources for an example).

You should be careful about memory consumption. In general, the kernel takes the attitude that if it was worth allocating, it's worth hanging onto; and so the program does not free much storage. Also, nearly all of the allocation happens during startup. Since a game may run for a very long time (thousands of turns perhaps), it is important not to run the risk of exhausting memory at a climactic moment in the game!

Also, the kernel should not exit on its own. The only permissible times are when the internal state is so damaged that interface error-handling routines (see below) cannot be called safely. Such situations are rare. If you add something to the kernel and need to handle error situations, then you should call one of the interface's error-handling routines. There are distinct routines for problems during initializations vs problems while running, and both error and warning routines. Warning routines may return, so kernel code should be prepared to continue on, while error routines will never return.

Writing New Synthesis Methods

You can add new synthesis methods to Xconq. This may be necessary if an external program does not exist, is unsuitable, or the external program interface is not available. Synthesis methods should start out by testing whether or not to run, and should never assume that any other method has been run before or after, nor that any particular game module has been loaded. However, "tricks" are usually OK, such as setting a particular global variable in a particular module only, then having the synthesis method test whether that global is set. See the file init.c for further details.

Synthesis methods that take longer than a second or two to execute should generate percent-done info for the interface to use, via the function announce_progress. Be aware that most methods will be O(n) or O(n*n) on the size of the world or the number of units, so they can take much longer to set up a large game than a small one. Players will often go overboard and start up giant games, so this happens frequently. Also, Xconq may be running on a much smaller and slower machine than what you're using now.

Writing New Namers

[describe hook and interface]

Writing New AIs

You can add new types of AIs to Xconq. You would do this to add different strategies as well as to add AIs that are programmed specifically for a single game or class of games. (This is useful because the generic AI does not always understand the appropriate strategy for each game.)

You have to design the object that is the AI's "mental state". If your AI need only react to the immediate situation, then this object can be very simple, but in general you will need to design a fairly elaborate structure with a number of substructures. Since there may be several AIs in a single game, you should be careful about using globals, and since Xconq games may often run for a long time, you should be careful not to consume memory recklessly.

Note that these functions have very few constraints, so you can write them to work together in various ways. For instance, an AI can decide whether to resign once/turn, once/action, or once for each 4 units it moves, every other turn.

[describe default AI as illustrative example]

Extending GDL

GDL has been designed so as to be relatively easily extensible. I say "relatively" because although it is quite easy to define a new keyword or table, it is not always so easy to integrate the implementation code into the kernel correctly.

Instead of actually changing GDL, you can experiment with an addition by using the extensions property of unit, material, and terrain types. In the code, you call get_u_extension, pass it the type, name of the property, and a default to return if the value was not given. In the game definition, the designer would say (unit town (extensions (my-ext xxx))).

[show examples for global, property, table, event, task]

The file gvar.def defines all the global variables.

The file utype.def defines all the unit type properties.

From time to time, it may be worthwhile to extend unit objects. This should be rare, because games may have thousands of units, and each unit requires at least 100 bytes of storage already, so you should avoid making them any larger. Properties of an individual unit are scattered through keyword.def. Once the structure slot is added, you just need to add reading and writing of the value, using the K_xxx enum that was defined with the keyword. You should attempt to make a reasonable default and use it to avoid writing out the value, so as to save time when Xconq reads a game in.

GDL symbols beginning with zz- should be reserved for the use of AI code. You may want to add some of these, either to serve as a convenient place for AIs to cache the results of their analyis of a game, or else as a way for game designers to add "hints" for AIs that know to look at them.

Note that all the `*.def' files together are to define the exact set of symbols defined by GDL. You should not add any expedient matching on symbols or searching for particular symbols without adding them to an appropriate `.def' file.


Go to the first, previous, next, last section, table of contents.